import { dracula } from '@code-surfer/themes'
import { CodeSurfer } from 'code-surfer'
export const theme = { ...dracula, styles: { ...dracula.styles, a: { color: 'fuchsia'}, h1: {color: 'aqua'}, h2: {color: 'aqua'}, h3: {color: 'aquamarine'}} }

# ReactJS 入门

  by 王凯

---


## 开发环境准备

  - nodejs v10+  [http://npm.taobao.org/mirrors/node/](http://npm.taobao.org/mirrors/node/)

  - 设置PATH  windows: 此电脑 -> 属性 -> 环境变量 -> 系统变量 -> 编辑PATH -> 增加nodejs安装目录

  - ```npm set config registry https://registry.npm.taobao.org```

  - ```npx nrm use taobao```


## slide和demo库地址

  [https://gitee.com/kayw7/react-tutorial](https://gitee.com/kayw7/react-tutorial)

  git clone / git pull

---

### demo 00

<Steps>

  - reactjs in html
  - html in reactjs

</Steps>

---

### demo 02

  - npx create-react-app react-webapp --use-npm

  - learn-react-todos

  <Notes>completed hide delete button</Notes>
  <Notes>debug webpack</Notes>
---

### JSX

 - [babeljs](https://babeljs.io/repl) 转译成 React.createElement

 - ```<XX>{children}</XX>``` XX对应html元素或自定义组件名, 无children节点时直接写```<XX />```

 - html元素属性 camelCase: tabindex => tabIndex class => className for => htmlFor  例外: aria-*  data-*

 - html dom事件 camelCase:  click => onClick  change => onChange

 - {} 可以放任意可执行JS语句

 - 数组元素加key

 - 组件函数/class render不能返回多于1个JSX元素, 包裹于```<div>```或者```<React.Fragment>```(简写: ``` <></> ```)

 - 注释 {/* */}

 - ``` import React from 'react' ``` 组件内必须要写

---

<CodeSurfer>

```jsx
import React, {useState} from 'react';

export default function TodoContainer() {
  const [todos, setTodos] = useState([{
    id: 1,
    title: '学习JSX',
    completed: false
  }, {
    id: 2,
    title: '学习Component写法',
    completed: false
  }, {
    id: 3,
    title: '学习useState',
    completed: false
  }, {
    id: 4,
    title: '学习useEffect',
    completed: false
  }])
  const handleTodoComplete = (todo, checked) => {
    const newTodos = todos.map(td => {
      if (td.id === todo.id) {
        return { ...td, completed: checked};
      }
      return td;
    });
    setTodos(newTodos);
  }
  return (
        <div>
          {todos.map(todo => (
            <li key={todo.id}><input type="checkbox" checked={todo.completed} onChange={(ev) => {
              handleTodoComplete(todo, ev.target.checked)
            }} /><button>删除</button>{todo.title}</li>
          ))}
      </div>
  )
}
```

```jsx 1:3 subtitle="es6 import export / react函数组件定义"
import React, {useState} from 'react';

export default function TodoContainer() {
  const [todos, setTodos] = useState([{
    id: 1,
    title: '学习JSX',
    completed: false
  }, {
    id: 2,
    title: '学习Component写法',
    completed: false
  }, {
    id: 3,
    title: '学习useState',
    completed: false
  }, {
    id: 4,
    title: '学习useEffect',
    completed: false
  }])
  const handleTodoComplete = (todo, checked) => {
    const newTodos = todos.map(td => {
      if (td.id === todo.id) {
        return { ...td, completed: checked}
      }
      return td
    })
    setTodos(newTodos)
  }
  return (
        <div>
          {todos.map(todo => (
            <li key={todo.id}><input type="checkbox" checked={todo.completed} onChange={(ev) => {
              handleTodoComplete(todo, ev.target.checked)
            }} /><button>删除</button>{todo.title}</li>
          ))}
      </div>
  )
}
```

```jsx 4:20 title="useState hook" subtitle="[todos, setTodos] array destructuring 解构数组 const todos = states[0] const setTodos = state[1]  var => let const"
import React, {useState} from 'react';

export default function TodoContainer() {
  const [todos, setTodos] = useState([{
    id: 1,
    title: '学习JSX',
    completed: false
  }, {
    id: 2,
    title: '学习Component写法',
    completed: false
  }, {
    id: 3,
    title: '学习useState',
    completed: false
  }, {
    id: 4,
    title: '学习useEffect',
    completed: false
  }])
  const handleTodoComplete = (todo, checked) => {
    const newTodos = todos.map(td => {
      if (td.id === todo.id) {
        return { ...td, completed: checked}
      }
      return td
    })
    setTodos(newTodos)
  }
  return (
        <div>
          {todos.map(todo => (
            <li key={todo.id}><input type="checkbox" checked={todo.completed} onChange={(ev) => {
              handleTodoComplete(todo, ev.target.checked)
            }} /><button>删除</button>{todo.title}</li>
          ))}
      </div>
  )
}
```

```jsx 31:38 subtitle="数组map生成多个jsx key必须"
import React, {useState} from 'react';

export default function TodoContainer() {
  const [todos, setTodos] = useState([{
    id: 1,
    title: '学习JSX',
    completed: false
  }, {
    id: 2,
    title: '学习Component写法',
    completed: false
  }, {
    id: 3,
    title: '学习useState',
    completed: false
  }, {
    id: 4,
    title: '学习useEffect',
    completed: false
  }])
  const handleTodoComplete = (todo, checked) => {
    const newTodos = todos.map(td => {
      if (td.id === todo.id) {
        return { ...td, completed: checked}
      }
      return td
    })
    setTodos(newTodos)
  }
  return (
        <div>
          {todos.map(todo => (
            <li key={todo.id}><input type="checkbox" checked={todo.completed} onChange={(ev) => {
              handleTodoComplete(todo, ev.target.checked)
            }} /><button>删除</button>{todo.title}</li>
          ))}
      </div>
  )
}
```

```jsx 21:29 subtitle="onChange事件响应 setTodos更新todos 不可变性 不能直接修改todos  setTodos异步更新"
import React, {useState} from 'react';

export default function TodoContainer() {
  const [todos, setTodos] = useState([{
    id: 1,
    title: '学习JSX',
    completed: false
  }, {
    id: 2,
    title: '学习Component写法',
    completed: false
  }, {
    id: 3,
    title: '学习useState',
    completed: false
  }, {
    id: 4,
    title: '学习useEffect',
    completed: false
  }])
  const handleTodoComplete = (todo, checked) => {
    const newTodos = todos.map(td => {
      if (td.id === todo.id) {
        return { ...td, completed: checked}
      }
      return td
    })
    setTodos(newTodos)
  }
  return (
        <div>
          {todos.map(todo => (
            <li key={todo.id}><input type="checkbox" checked={todo.completed} onChange={(ev) => {
              handleTodoComplete(todo, ev.target.checked)
            }} /><button>删除</button>{todo.title}</li>
          ))}
      </div>
  )
}
```

</CodeSurfer>

---

### Day1 Homework

  1. 用create-react-app创建react-todo-app项目
  2. 仿照demo02代码显示待办事项
  3. 完成删除按钮点击事件响应(onClick), 删除后界面不显示该todo项

---

### demo 03

 <UserProfile />

 - fetch ```https://jsonplaceholder.typicode.com/users/1``` 获取用户信息
 - useEffect 触发fetch
 - user信息页面显示

 目标:
   * 数据动态ajax请求
   * useEffect hook了解

---

### useEffect

---

<CodeSurfer>

```jsx
import React, { useState, useEffect } from 'react'

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null)

  function handleStatusChange(status) {
    setIsOnline(status.isOnline)
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange)

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange)
    }
  }, [props.friend.id])

  if (isOnline === null) {
    return 'Loading...'
  }
  return isOnline ? 'Online' : 'Offline'
}
```

```jsx 10:16 subtitle='This effect handles the online state subscription'
import React, { useState, useEffect } from 'react'

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null)

  function handleStatusChange(status) {
    setIsOnline(status.isOnline)
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange)

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange)
    }
  }, [props.friend.id])

  if (isOnline === null) {
    return 'Loading...'
  }
  return isOnline ? 'Online' : 'Offline'
}
```

```jsx 13:15 subtitle='Returned function will be fired on unmount (cleanup)'
import React, { useState, useEffect } from 'react'

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null)

  function handleStatusChange(status) {
    setIsOnline(status.isOnline)
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange)

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange)
    }
  }, [props.friend.id])

  if (isOnline === null) {
    return 'Loading...'
  }
  return isOnline ? 'Online' : 'Offline'
}
```

```jsx 16[6:22] subtitle='Effect will run only when this friend ID changes'
import React, { useState, useEffect } from 'react'

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null)

  function handleStatusChange(status) {
    setIsOnline(status.isOnline)
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange)

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange)
    }
  }, [props.friend.id])

  if (isOnline === null) {
    return 'Loading...'
  }
  return isOnline ? 'Online' : 'Offline'
}
```

```jsx 1:22
import React, { useState, useEffect } from 'react'

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null)

  function handleStatusChange(status) {
    setIsOnline(status.isOnline)
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange)

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange)
    }
  }, [props.friend.id])

  if (isOnline === null) {
    return 'Loading...'
  }
  return isOnline ? 'Online' : 'Offline'
}
```

</CodeSurfer>

---

### React Hooks

  1. 只能用于函数式定义组件, 一个组件函数内可定义多个hooks(如多个useState, useEffect)
  2. 需要定义在函数开头位置,不能放在if for等控制结构内
  3. useEffect 第一个参数为函数, 其返回值为函数用于组件unmount或重新触发时先清除timer/cleanup某些数据等
  4. useEffect 第二个参数为依赖变量数组, 参数不填时每次组件state props变化均触发, 为空数组时首次组件render时调用
     *依赖数组内任一对象变化均会触发effect函数*

---

### More Hooks

  - useMemo useCallback 缓存计算量较大对象,提高响应速度
  - useRef 直接引用变量可指向dom对象等
  - [...](https://zh-hans.reactjs.org/docs/hooks-reference.html)
  - useXXX  使用React内置hooks基础上自定义hook, 提高代码复用

---

### Day2 homework

  - Day1基础上增加TodoFetchContainer
  - 数据源取https://jsonplaceholder.typicode.com/todos

  - TodoFetchContainer和TodoContainer抽取共用的TodoItem组件

---

### React常用库

  - [React Router](https://react-router.docschina.org/web/guides/quick-start) 路由管理   npm install react-router-dom

  - [ant design](https://ant-design.gitee.io/docs/react/getting-started-cn) 中后台UI组件库  npm install antd

  - [zarm-ui](https://zarm.gitee.io) 移动端组件库

### demo 04

  demo03里面TodoContainer UserProfile分成/todo  /user 两个路由

  antd menu 作导航菜单

---

### Day3 Homework

  1.将前面TodoContaner TodoFetchContainer用react-router定义为/todo  /fetchtodo两个路由页面

  2.导航栏选择使用antd layout示例中任意一个

  3.用antd Checkbox Input Button等组件写一个TodoAntdContainer, 路由/antdtodo
---
## class component 定义
---
<CodeSurfer>

```jsx
import React, { Component } from 'react'

class Example extends Component {
  constructor() {
    this.state = { events: [] }
    this.handleChange = this.handleChange.bind(this)
  }

  componentDidMount() {
    console.log('didmount', this.state)
  }
  componentDidUpdate() {
    console.log('didupdate', this.state)
  }
  componentWillUnmount() {
    console.log('umount', this.state)
  }
  handleChange(ev) {
    console.log(ev)
  }

  render() {
    return (
      <ul>
        {this.props.user}
        {this.state.events.map(({ id, name }) => (
          <button key={id} onClick={this.handleChange}>{name}</button>
        ))}
      </ul>
    )
  }
}

Example.propTypes = {
  user: PropTypes.string
}
```

</CodeSurfer>

---

### 组件/页面间数据交互

  1. 父子组件
  2. 兄弟组件
  3. 祖祖父子组件
  4. 不同路由页面

 - props
 - 函数组件:
   * [useReducer](https://zh-hans.reactjs.org/docs/hooks-reference.html#usereducer)
   * [iostore](https://github.com/yisbug/iostore)  [icestore](https://github.com/ice-lab/icestore)
 - class组件或函数组件:
   * [redux](https://redux.js.org/)
   * [mobjs](https://mobx.js.org/)
   * [rematch](https://github.com/rematch/rematch)

  <Notes>TodoItem props</Notes>

---

 3个作业整体一个project, 可以用gitee github bitbucket 项目地址 或一个zip包发到我企业微信

---

## 谢谢
